using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using Boo.Lang.Compiler.Ast;
using Module=Boo.Lang.Compiler.Ast.Module;

namespace Photon.Dsl
{
	/// <summary>
	/// Registration class that allows to mix multiple base classes with multiple
	/// compiler sources in one pass
	/// </summary>
	public sealed class BaseClassInfo
	{
		/// <summary>
		/// Base class being used for the Dsl
		/// </summary>
		public Type BaseClass { get; private set; }

		private readonly List<ParameterDeclaration> _parameters = new List<ParameterDeclaration>();
		private Func<Module, string> _naming;
		internal string MethodName { get; private set; }

		private readonly List<string> _defaultNamespaces = new List<string>();
		private List<Assembly> _references = new List<Assembly>();

		/// <summary>
		/// Initializes a new instance of the <see cref="BaseClassInfo"/> class.
		/// </summary>
		/// <param name="type">The type.</param>
		public BaseClassInfo(Type type)
		{
			BaseClass = type;
			MethodName = "Execute";
			_naming = m => m.FullName;
		}

		/// <summary>
		/// Retrieves list of available constructors for the base class
		/// </summary>
		/// <returns></returns>
		public IEnumerable<ConstructorInfo> GetCtors()
		{
			var ctors = BaseClass.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
			foreach (var ctorInfo in ctors)
			{
				if (ctorInfo.IsPrivate)
				{
					continue;
				}
				yield return ctorInfo;
			}
		}

		internal IEnumerable<string> Imports
		{
			get { return _defaultNamespaces.AsReadOnly(); }
		}

		/// <summary>
		/// Gets the name of the class for the provided module.
		/// </summary>
		/// <param name="module">The module.</param>
		/// <returns></returns>
		[CLSCompliant(false)]
		public string GetClassName(Module module)
		{
			return _naming(module);
		}

		/// <summary>
		/// Allows to add named parameter to the target method
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="name">The name.</param>
		/// <returns>Same instance for the inlining</returns>
		public BaseClassInfo WithParameter<T>(string name)
		{
			Enforce.ArgumentNotNullOrEmpty(name, "name");
			_parameters.Add(new ParameterDeclaration(name, new SimpleTypeReference(typeof(T).FullName)));
			return this;
		}

		///<summary>
		/// Retrieves list of the parameters for the target method
		///</summary>
		public IList MethodParameters
		{
			get { return _parameters.AsReadOnly(); }
		}

		/// <summary>
		/// Allows to override the default naming function
		/// </summary>
		/// <param name="func">The func.</param>
		/// <returns>Same instance for the inlining</returns>
		[CLSCompliant(false)]
		public BaseClassInfo Naming(Func<Module, string> func)
		{
			Enforce.ArgumentNotNull(func, "func");
			_naming = func;
			return this;
		}

		/// <summary>
		/// Adds the specified namespace to list of default imports
		/// </summary>
		/// <param name="names">The name.</param>
		/// <returns></returns>
		public BaseClassInfo Importing(params string[] names)
		{
			Enforce.ArgumentNotNull(names, "names");

			foreach (var name in names)
			{
				Enforce.NotNullOrEmpty(name,"name");
				_defaultNamespaces.Add(name);
			}
			
			return this;
		}

		/// <summary>
		/// Referencings the specified assembly.
		/// </summary>
		/// <param name="assemblies">The assembly.</param>
		/// <returns></returns>
		public BaseClassInfo Referencing(params Assembly[] assemblies)
		{
			_references.AddRange(assemblies);
			return this;
		}

		/// <summary>
		/// Gets the references.
		/// </summary>
		/// <value>The references.</value>
		public IEnumerable<Assembly> References
		{
			get { return _references.AsReadOnly(); }
		}
	}
}